home *** CD-ROM | disk | FTP | other *** search
- {---------------------------------------------------------}
- { Project : Call Stack Reporter }
- { Auteur : Ir. G.W. van der Vegt }
- { Hondsbroek 57 }
- { 6121 XB Born }
- {---------------------------------------------------------}
- { Datum .tijd Revisie }
- { 920713.2100 Creatie. }
- { 920715.2330 Trace at normal exit (exitcode=0) removed.}
- { 920805.2230 Path removed from filename in trace }
- { 920806.2200 Blanks filled in, RunTime Library routines}
- { now traced to. }
- { 921026.2000 Textmode(lastmode) added to default }
- { Csr_report. Objects & overlay tracing }
- { tested. }
- { 921118.1400 Exitcode doesn't trigger trace anymore }
- { 931114.1430 Keyboard flush in exitprocedure }
- { 940201.2200 Made independed of Routines. }
- {---------------------------------------------------------}
- { To do Trace Virtual Methode Table (VMT) }
- {---------------------------------------------------------}
-
- {$D+}
- {$L+}
-
- {---------------------------------------------------------}
- {----This unit gives the line numbers & filenames at error}
- { The result is a list of the call stack as produced by}
- { the Turbo Pascal IDE. }
- { }
- { The internal text mode report function can be }
- { replaced by another one located in your program. }
- { This could be a graphics mode or printer version. It }
- { must be compiled far (so use $F+ & $F- around it. }
- { It's called once for each call level. }
- { }
- { This program parses the MAP file to obtain the }
- { line numbers. It searches for the MAP file in the }
- { programs startup directory as obtained by }
- { PARAMSTR(0). }
- {---------------------------------------------------------}
- { To obtain all possible info compile with the }
- { following setting : }
- { }
- { OPTIONS/LINKER/MAP FILE = DETAILED }
- { OPTION/COMPILE/DEBUG INFO = ON }
- { }
- { The last can also be forced by the $D+ compiler }
- { directive . }
- { }
- { This version traces procedures, functions through }
- { the main program and it's (overlayed) units. It also }
- { traces static methodes but not virtual methodes. }
- { When tracing static methodes a phantom entry with }
- { an call address located oon the heap is generated. }
- { The trace is stopped at the first call to a virtual }
- { methode. In a future version VMT tracing will be }
- { added as soon as I start using virtual methodes. }
- {---------------------------------------------------------}
-
- UNIT CSR_01;
-
- INTERFACE
-
- {---------------------------------------------------------}
- {----TYPES }
- {---------------------------------------------------------}
-
- TYPE
- Csr_repfunc = PROCEDURE(level : Word;csr : STRING);
-
- {---------------------------------------------------------}
- {----VARIABLES }
- {---------------------------------------------------------}
-
- VAR
- Csr_reporter : Csr_repfunc;
-
- {---------------------------------------------------------}
- {----PROCEDURES/FUNCTIONS }
- {---------------------------------------------------------}
-
- PROCEDURE Csr_report(level : Word;csr : STRING);
-
- {---------------------------------------------------------}
-
- IMPLEMENTATION
-
- Uses
- CRT,
- DOS;
-
- VAR
- ext : extstr;
- dir : dirstr;
- nam : namestr;
- mapfile : BOOLEAN;
- map : Text;
- ft : BOOLEAN;
-
- CONST
- space = #32;
-
- {---------------------------------------------------------}
- {----SUPPORT PROCEDURES & FUNCTIONS }
- {---------------------------------------------------------}
-
- FUNCTION Istr(i,n : INTEGER;pad : CHAR) : STRING;
-
- VAR
- s : STRING;
-
- BEGIN
- Str(i:n,s);
- IF (pad<>space)
- THEN
- WHILE (Pos(space,s)>0) DO
- s[Pos(space,s)]:=pad;
- Istr:=s;
- END; {of Istr}
-
- {---------------------------------------------------------}
-
- FUNCTION Wstr(w : WORD;n : INTEGER) : STRING;
-
- VAR
- s : STRING;
-
- BEGIN
- Str(w:n,s);
- Wstr:=s;
- END; {of Wstr}
-
- {---------------------------------------------------------}
-
- FUNCTION Sstr(s : STRING;n : INTEGER) : STRING;
-
- VAR
- tmp : STRING;
-
- BEGIN
- tmp:=s;
- IF n>=0
- THEN WHILE (Length(tmp)<+n) DO Insert(space,tmp,1)
- ELSE WHILE (Length(tmp)<-n) DO tmp:=tmp+space;
- Sstr:=tmp;
- END; {of Sstr}
-
- {---------------------------------------------------------}
-
- PROCEDURE Beep;
-
- BEGIN
- Sound(500);
- Delay(20);
- Nosound;
- END; {of Beep}
-
- {---------------------------------------------------------}
-
- FUNCTION Word2Hex(w : Word) : STRING;
-
- const
- hexChars : array [0..$F] of Char = '0123456789ABCDEF';
-
- begin
- Word2Hex :=hexChars[Hi(w) shr 4]+hexChars[Hi(w) and $F]+
- hexChars[Lo(w) shr 4]+hexChars[Lo(w) and $F];
- end; {of Word2Hex}
-
- {---------------------------------------------------------}
-
- Function Hex2Word(h : String) : word;
-
- const
- hexChars : String[16] = '0123456789ABCDEF';
-
- var
- f : word;
-
- begin
- f := 0;
- while length(h) > 0 do
- begin
- if pos(Copy(h,1,1),HexChars) = 0
- then f := 0
- Else f := (f*16)+pos(H[1],Hexchars)-1;
- delete(h,1,1);
- end;
- Hex2Word:= f;
- end; {of Hex2Word}
-
- {---------------------------------------------------------}
-
- FUNCTION Ptr2Hex(p : POINTER) : STRING;
-
- BEGIN
- IF (p=nil)
- THEN Ptr2Hex := ' NIL '
- else Ptr2Hex := Word2hex(Seg(P^))+':'+Word2hex(Ofs(P^));
- END; {of Ptr2Hex}
-
- {---------------------------------------------------------}
-
- Procedure FlushKbd;
-
- Begin
- MemW[$40:$1C]:=MemW[$40:$1A];
- End; {of Fluskkbd}
-
- {---------------------------------------------------------}
- {----STACK TRACE ROUTINES START HERE }
- {---------------------------------------------------------}
-
- FUNCTION BPreg : WORD;
-
- INLINE($55/$58); {Push BP, Pop AX}
-
- {---------------------------------------------------------}
-
- Procedure Findlineno(first,near : BOOLEAN;dep : Word;p : Pointer);
-
- VAR
- tmp : String[80];
-
- line : Integer;
- adr : String[9];
- ch : Char;
-
- fn : STRING[80];
- un : STRING[80];
-
- errseg,
- errofs : Word;
-
- s,
- lastun,
- lastpr,
- lastfn : STRING[80];
- lastnr : Word;
- call : STRING[4];
-
- BEGIN
- IF near
- THEN call:='near'
- ELSE call:='far ';
-
- errseg:=Hex2word(Copy(Ptr2hex(p),1,4));
- errofs:=Hex2word(Copy(Ptr2hex(p),6,4));
-
- lastnr:=0;
- lastfn:='';
- lastpr:='';
- lastun:='';
-
- Assign(map,dir+nam+'.MAP');
- {$I-} Reset(map); {$I+}
- IF (IOResult=0)
- THEN
- BEGIN
- {----Fist try on unit/program name}
- s:='';
- {
- 00000H 00096H 00097H VALTOREN CODE
-
- Address Publics by Value
- }
- WHILE NOT(Eof(map) OR
- (Pos('Publics by Value',s)>0) OR
- (Pos('Line numbers' ,s)>0)) DO
- BEGIN
- Readln(map,s);
- IF (Length(s)>=45) AND (s[7]='H')
- THEN
- BEGIN
- IF (Errseg=Hex2Word(Copy(s,2,4))) {AND
- (Copy(s,42,4)='CODE')}
- THEN lastun:=Copy(s,23,18);
- END;
- END;
-
- {----Strip Trailing Blanks}
- WHILE (Length(lastun)>0) AND
- (lastun[Length(lastun)]=#32) DO
- Delete(lastun,Length(lastun),1);
-
- {----Second Try to find procedure name}
- s:='';
- {
- Address Publics by Value
-
- 0000:0000 @
- 000A:00CB MENU_INIT
- }
- WHILE NOT(Eof(map) OR
- (Pos('Line numbers',s)>0)) DO
- BEGIN
- Readln(map,s);
- IF (Length(s)>=18) AND (s[6]=':')
- THEN
- BEGIN
- IF (Errseg=Hex2Word(Copy(s,2,4)))
- THEN
- BEGIN
- IF (lastpr='')
- THEN lastpr:=Copy(s,18,Length(s)-17)
- ELSE
- IF (Errofs>=Hex2Word(Copy(s,7,4)))
- THEN lastpr:=Copy(s,18,Length(s)-17);
- END;
- END;
- END;
-
- {----Strip Trailing Blanks}
- WHILE (Length(lastpr)>0) AND
- (lastpr[Length(lastpr)]=#32) DO
- Delete(lastpr,Length(lastpr),1);
-
- {----Third try on line numbers & sourcefile names}
- REPEAT
- {
- Line numbers for TEST_ERROR(TEST_ERR.PAS) segment TEST_ERROR
- }
- IF (Pos('Line numbers',s)>0)
- THEN
- BEGIN
- Delete(s,1,17);
- un:=Copy(s,1,Pos('(',s)-1);
- Delete(s,1,Pos('(',s));
- fn:=Copy(s,1,Pos(')',s)-1);
-
- While Pos('\',fn)>0 DO Delete (fn,1,Pos('\',fn));
-
- Readln(map);
- REPEAT
- {
- 15 0000:0008 16 0000:0017 18 0000:00C4 28 0000:00D2
- }
- Read(map,line);
- Read(map,ch);
- Read(map,adr);
- IF (Errseg=Hex2Word(Copy(adr,1,4)))
- THEN
- BEGIN
- lastfn:=fn;
- IF (Errofs>=Hex2Word(Copy(adr,6,4)))
- THEN lastnr:=line;
- END;
-
- If Eoln(map)
- Then Readln(map);
-
- UNTIL Eoln(map);
- END;
-
- IF NOT(eof(map))
- THEN Readln(map,s);
-
- UNTIL Eof(map) OR ((lastnr<>0) OR (lastfn<>''));
-
- Close(map);
-
- Beep;
-
- IF (lastfn<>'') AND ((errseg<>0) OR (errofs<>0))
- THEN
- {----Report Line Number & Source File}
- BEGIN
- WHILE (length(lastfn)<12) DO Insert(#32,lastfn,1);
- If first
- THEN
- Csr_reporter(dep,'Runtime error '+Istr(exitcode,3,'0')+
- ' in line '+Wstr(lastnr,4)+
- ' of '+lastfn+
- ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.')
- ELSE
- Csr_reporter(dep,' Called '+call+' from line '+Wstr(lastnr,4)+
- ' of '+lastfn+
- ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.');
- END
- ELSE
- BEGIN
- IF (lastun<>'') OR (lastpr<>'')
- THEN
- {----Report Unit/Program Name & Procedure name}
- BEGIN
- IF (Pos('@',lastpr)>0)
- THEN s:=lastun+'.MAIN'
- ELSE s:=lastun+'.'+lastpr;
-
- WHILE (Length(s)>25) DO
- Delete(s,Length(s),1);
-
- If first
- THEN
- Csr_reporter(dep,'Runtime error '+Istr(exitcode,3,'0')+
- ' in '+Sstr(s,25)+
- ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.')
- ELSE
- Csr_reporter(dep,' Called '+call+' from '+Sstr(s,25)+
- ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.');
- END
- ELSE
- {----Report Error Address Only}
- BEGIN
- If first
- THEN
- Csr_reporter(dep,'Runtime error '+Istr(exitcode,3,'0')+
- ' '+
- ' '+
- ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.')
- ELSE
- Csr_reporter(dep,' Called '+call+' from line '+
- ' '+
- ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.');
- END;
- END;
- END
- ELSE
- {----Report Error Addres Only}
- Csr_reporter(dep,'Runtime error '+Istr(exitcode,0,'0')+
- ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.')
- END; {of Findlineno}
-
- {---------------------------------------------------------}
- {$F+}
-
- VAR
- exitsave : POINTER;
-
- PROCEDURE Myexit;
-
- VAR
- ch : Char;
- cdiv,
- csmin,
- cs,
- sp,
- ss : WORD;
- p : Pointer;
- dep : WORD;
- j : INTEGER;
-
- BEGIN
- Flushkbd;
-
- Exitproc:=exitsave;
-
- IF (exitcode=0) OR (erroraddr=NIL) THEN Exit;
-
- sp:=BPreg;
- ss:=SSeg;
-
- {----Calculate calling depth}
- dep:=0;
- p:=Ptr(ss,sp);
- WHILE MemW[ss:Ofs(p^)]<>0 DO
- BEGIN
- IF (Mem[cs:MemW[ss:Ofs(p^)+2]-3]<>$E8)
- THEN cs:=MemW[ss:Ofs(p^)+4];
-
- p:=Ptr(ss,MemW[ss:Ofs(p^)]);
- Inc(dep);
- END;
-
- p:=Ptr(ss,sp);
- cdiv :=Cseg-cs;
- csmin:=cs;
- cs :=Cseg;
-
- {----Report Runtime address}
- Findlineno(true,true,dep,erroraddr);
- Dec(dep);
-
- {----Calculate cseg at runtime error}
- cs:=csmin+Seg(erroraddr^);
-
- {----Prevent Turbo Pascal from reporting}
- Erroraddr:=NIL;
-
- If NOT(mapfile) THEN Exit;
-
- {----Skip Runtime error handler entry}
- IF (MemW[ss:Ofs(p^)]<>0)
- THEN p:=Ptr(ss,MemW[ss:Ofs(p^)]);
-
- {----Report Call Stack}
- WHILE MemW[ss:Ofs(p^)]<>0 DO
- BEGIN
- {----Test for near call instruction 3 bytes before return address}
- IF (Mem[cs:MemW[ss:Ofs(p^)+2]-3]=$E8)
- {----Trace a near call}
- THEN Findlineno(false,true,dep,Ptr(WORD(Cs+Cdiv-Cseg),MemW[ss:Ofs(p^)+2]-3))
- ELSE
- {----Trace a far call}
- BEGIN
- Cs:=MemW[ss:Ofs(p^)+4];
- Findlineno(false,false,dep,Ptr(WORD(Cs+Cdiv-Cseg),MemW[ss:Ofs(p^)+2]-3));
- END;
-
- {----Increment stackpointer}
- p:=Ptr(ss,MemW[ss:Ofs(p^)]);
- Dec(dep);
- END;
-
- END; {of Myexit}
-
- {---------------------------------------------------------}
-
- PROCEDURE Csr_report(level : Word;csr : STRING);
-
- BEGIN
- IF ft
- THEN
- BEGIN
- Textmode(lastmode);
- ft:=false;
- END;
- Writeln(csr+' (',level,')');
- END; {of Csr_report}
- {$F-}
- {---------------------------------------------------------}
-
- BEGIN
- exitsave:=Exitproc;
- exitproc:=@Myexit;
- csr_reporter:=Csr_report;
-
- Fsplit(Paramstr(0),dir,nam,ext);
- Assign(map,dir+nam+'.MAP');
- {$I-} Reset(map); {$I+}
- IF (IOResult=0)
- THEN
- BEGIN
- mapfile:=true;
- Close(map);
- END
- ELSE mapfile:=false;
-
- ft:=true;
- END.
-
- { STACK UNIT NEEDED FOR CRS_01}
-
- UNIT Stack1;
-
- INTERFACE
-
- PROCEDURE test2(VAR i : Integer);
-
- IMPLEMENTATION
-
- VAR
- i : INTEGER;
-
- {---------------------------------------------------------}
-
- PROCEDURE test2(VAR i : Integer);
-
- PROCEDURE test4(i : INTEGER);
-
- VAR
- tmp : Integer;
-
- BEGIN
- tmp:=0;
- i:=1 div tmp;
- END;
-
- BEGIN
- test4(i);
- END;
-
- {---------------------------------------------------------}
-
- BEGIN
- i:=1;
- END.
-
-
- { ------------------------------- DEMO ------------------------}
- {---------------------------------------------------------}
- PROGRAM Csrtst;
-
- USES
- CRT,
- Csr_01,
- Stack1;
-
- {---------------------------------------------------------}
-
- PROCEDURE test3;
-
- VAR
- i : INTEGER;
-
- BEGIN
- test2(i);
- END;
-
- {---------------------------------------------------------}
-
- PROCEDURE test4;
-
- BEGIN
- test3
- END;
-
- {---------------------------------------------------------}
-
- BEGIN
- clrscr;
- test4;
- END.